home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 January: Mac OS SDK / Dev.CD Jan 99 SDK1.toast / Development Kits / Apple Guide / Engineering / APISample / APISampleMPW / TApplication / TApplication.cp < prev    next >
Encoding:
Text File  |  1992-08-29  |  15.9 KB  |  534 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple Application Framework
  6. #
  7. #    TApplication
  8. #
  9. #    TApplication.cp        -    C++ source
  10. #
  11. #    Copyright © 1989 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    
  15. #            1.20                    10/91
  16. #            1.10                     07/89
  17. #            1.00                     04/89
  18. #
  19. #    Components:
  20. #            TApplicationCommon.h    July 9, 1989
  21. #            TApplication.h            July 9, 1989
  22. #            TApplication.cp            July 9, 1989
  23. #            TApplication.r            July 9, 1989
  24. #            TDocument.h                July 9, 1989
  25. #
  26. #    TApplication is a rudimentary application framework
  27. #    for C++. The applications CPlusShapesApp and CPlusTESample
  28. #    are built using CPlusAppLib.
  29. #
  30. ------------------------------------------------------------------------------*/
  31.  
  32.  
  33. /*
  34. Segmentation strategy:
  35.  
  36.     This program has only one segment, since the issues
  37.     surrounding segmentation within a class's methods have
  38.     not been investigated yet. We DO unload the data
  39.     initialization segment at startup time, which frees up
  40.     some memory 
  41.  
  42. SetPort strategy:
  43.  
  44.     Toolbox routines do not change the current port. In
  45.     spite of this, in this program we use a strategy of
  46.     calling SetPort whenever we want to draw or make calls
  47.     which depend on the current port. This makes us less
  48.     vulnerable to bugs in other software which might alter
  49.     the current port (such as the bug (feature?) in many
  50.     desk accessories which change the port on OpenDeskAcc).
  51.     Hopefully, this also makes the routines from this
  52.     program more self-contained, since they don't depend on
  53.     the current port setting. 
  54.  
  55. Clipboard strategy:
  56.  
  57.     This program does not maintain a private scrap.
  58.     Whenever a cut, copy, or paste occurs, we import/export
  59.     from the public scrap to TextEdit's scrap right away,
  60.     using the TEToScrap and TEFromScrap routines. If we did
  61.     use a private scrap, the import/export would be in the
  62.     activate/deactivate event and suspend/resume event
  63.     routines. 
  64. */
  65.  
  66. // Mac Includes
  67. #include <Types.h>
  68. #include <QuickDraw.h>
  69. #include <Fonts.h>
  70. #include <Events.h>
  71. #include <Controls.h>
  72. #include <Windows.h>
  73. #include <Menus.h>
  74. #include <TextEdit.h>
  75. #include <Dialogs.h>
  76. #include <Desk.h>
  77. #include <Scrap.h>
  78. #include <ToolUtils.h>
  79. #include <Memory.h>
  80. #include <SegLoad.h>
  81. #include <Files.h>
  82. #include <OSUtils.h>
  83. #include <Traps.h>
  84.  
  85. #include "TApplication.h"    // use the local version. If you make changes
  86.                                             // that you've debugged and want other files
  87.                                             // to use, simple copy this header file into
  88.                                             // the C++ Includes folder (don't forget to
  89.                                             // copy the TApplication object library to the C++
  90.                                             // Libraries folder)!
  91.  
  92. // OSEvent is the event number of the suspend/resume and mouse-moved events sent
  93. // by MultiFinder. Once we determine that an event is an osEvent, we look at the
  94. // high byte of the message sent to determine which kind it is. To differentiate
  95. // suspend and resume events we check the resumeMask bit.
  96. const short kOsEvent = app4Evt;                // event used by MultiFinder
  97. const short kSuspendResumeMessage = 0x01;    // high byte of suspend/resume event message
  98. const short kClipConvertMask = 0x02;        // bit of message field clip conversion
  99. const short kResumeMask = 0x01;                // bit of message field for resume vs. suspend
  100. const short kMouseMovedMessage = 0xFA;        // high byte of mouse-moved event message
  101.  
  102. extern "C"
  103.     // from MPW standard library
  104.         void _DataInit(void);                // sets up A5 globals
  105. };
  106.  
  107. /***********************************************************************/
  108. //
  109. // TApplication class declarations
  110. //
  111. /***********************************************************************/
  112.  
  113. //-----------------------------------------------------------------------
  114. // TApplication::TApplication -     
  115. //
  116.     TApplication::TApplication( void )
  117.     {
  118.         SysEnvRec envRec;
  119.         long stkNeeded, heapSize;
  120.     
  121.         // initialize Mac Toolbox components
  122.             InitGraf((Ptr) &qd.thePort );
  123.             InitFonts();
  124.             InitWindows();
  125.             InitMenus();
  126.             TEInit();
  127.             InitDialogs((ResumeProcPtr) nil);
  128.             InitCursor();
  129.     
  130.         // Unload data segment: note that _DataInit must not be in Main!
  131.             UnloadSeg((ProcPtr) _DataInit);
  132.     
  133.         // ignore the error returned from SysEnvirons; even if an error occurred,
  134.         // the SysEnvirons glue will fill in the SysEnvRec
  135.             ( void ) SysEnvirons( curSysEnvVers, &envRec );
  136.     
  137.         // Are we running on a 128K ROM machine or better???
  138.             if ( envRec.machineType < 0 )
  139.                 BigBadError( kErrStrings,eWrongMachine );        // if not, alert & quit
  140.     
  141.         // if we need more stack space, get it now
  142.             stkNeeded = StackNeeded();
  143.             if (stkNeeded > StackSpace())
  144.             {
  145.                 // new address is heap size + current stack - needed stack
  146.                     SetApplLimit((Ptr) ((long) GetApplLimit() - stkNeeded + StackSpace()));
  147.             }
  148.     
  149.         // Check for minimum heap size
  150.             heapSize = (long) GetApplLimit() - (long) ApplicZone();
  151.             if ( heapSize < HeapNeeded())
  152.                 BigBadError( kErrStrings, eSmallSize );
  153.     
  154.         // expand the heap so new code segments load at the top
  155.             MaxApplZone();
  156.     
  157.         // allocate an empty document list
  158.             fDocList = new TDocumentList;
  159.     
  160.         // check to see if WaitNextEvent is implemented
  161.             fHaveWaitNextEvent = TrapAvailable( _WaitNextEvent, ToolTrap );
  162.     
  163.         // initialize our class variables
  164.             fCurDoc = nil;
  165.             fDone = false;
  166.             fInBackground = false;
  167.             fMouseRgn = nil;
  168.             fWhichWindow = nil;
  169.             
  170.     } /* TApplication (constructor) */
  171.  
  172.  
  173. //-----------------------------------------------------------------------
  174. // TApplication::ExitLoop -     we're quitting the application; let's
  175. //                                do whatever needs to be done to clean up.
  176. //                                In our case, there isn't much to be done.
  177. //
  178.     void TApplication::ExitLoop( void )
  179.     {
  180.         fDone = true;
  181.     }
  182.  
  183.  
  184. //-----------------------------------------------------------------------
  185. // TApplication::EventLoop -     keep track of events and handle them
  186. //                                appropriately.
  187. //
  188.     void TApplication::EventLoop( void )
  189.     {
  190.         int gotEvent;
  191.         EventRecord tEvt;
  192.     
  193.         SetUp();                    // call setup routine
  194.         DoIdle();                    // do idle once
  195.     
  196.         while (fDone == false)
  197.           {
  198.             // always set up fWhichWindow before doing anything
  199.                 fWhichWindow = FrontWindow();
  200.                 
  201.             // see if window belongs to a document
  202.                 fCurDoc = fDocList->FindDoc(fWhichWindow);
  203.                 
  204.             // make sure we always draw into correct window
  205.                 SetPort(fWhichWindow);
  206.     
  207.             // let's allow others to use the cpu if they need (Multifinder friendliness).
  208.                 DoIdle();            // call idle time handler. this lets apps blink the caret
  209.                                     // or whatever else they wish to do
  210.             
  211.             //     determine if an event has occurred and what to do about it
  212.                 if ( fHaveWaitNextEvent )
  213.                 {
  214.                     gotEvent = WaitNextEvent( everyEvent, &tEvt, SleepVal(), fMouseRgn );
  215.                 }
  216.                 else
  217.                 {
  218.                     SystemTask();
  219.                     gotEvent = GetNextEvent( everyEvent, &tEvt );
  220.                 }
  221.                 fTheEvent = tEvt;
  222.     
  223.             // make sure we got a real event
  224.                 if ( gotEvent )
  225.                 {
  226.                     AdjustCursor();
  227.                     switch (fTheEvent.what)
  228.                     {
  229.                         case mouseDown :    DoMouseDown();
  230.                                             break;
  231.                                             
  232.                         case mouseUp :        DoMouseUp();
  233.                                             break;
  234.                                             
  235.                         case keyDown :
  236.                         case autoKey :        DoKeyDown();
  237.                                             break;
  238.                                             
  239.                         case updateEvt :    DoUpdateEvt();                
  240.                                             break;
  241.                                             
  242.                         case diskEvt :        DoDiskEvt();
  243.                                             break;
  244.                                             
  245.                         case activateEvt :    DoActivateEvt();
  246.                                             break;
  247.                                             
  248.                         case kOsEvent :        DoOSEvent();
  249.                                             break;
  250.                                             
  251.                         default :            break;
  252.                         
  253.                     } // end switch (fTheEvent.what)
  254.                 }
  255.                 
  256.             // update the cursor shape as needed after the event
  257.                 AdjustCursor();
  258.         }
  259.         // call cleanup handler
  260.         CleanUp();
  261.         
  262.     }  /* TApplication::EventLoop */
  263.  
  264.  
  265. //-----------------------------------------------------------------------
  266. // TApplication::DoKeyDown -     simple routine to handle key presses.
  267. //
  268.     void TApplication::DoKeyDown( void )
  269.     {
  270.         char key;
  271.         long mResult;
  272.     
  273.         key = (char) ( fTheEvent.message & charCodeMask );
  274.         if (( fTheEvent.modifiers & cmdKey ) && ( fTheEvent.what == keyDown ))
  275.         {
  276.             // only do command keys if we are not autokeying
  277.                 AdjustMenus();                    // make sure menus are up to date
  278.                 mResult = MenuKey( key );
  279.                 if ( mResult != 0 )                // if it wasn't a menu key, pass it through
  280.                 {
  281.                     DoMenuCommand( HiWrd( mResult ), LoWrd( mResult ));
  282.                     return;
  283.                 }
  284.         }
  285.         
  286.         if ( fCurDoc != nil )
  287.         {
  288.             EventRecord tEvt;
  289.     
  290.             // we copy event record so that we don't pass reference to object field 
  291.             tEvt = fTheEvent;
  292.             fCurDoc->DoKeyDown( &tEvt );
  293.         }
  294.           
  295.     }  /* TApplication::DoKeyDown */
  296.  
  297.  
  298. //-----------------------------------------------------------------------
  299. // TApplication::DoActivateEvt -     a window is becoming active. if it is
  300. //                                    one of ours, call the document's activate
  301. //                                    routine.
  302. //
  303.     void TApplication::DoActivateEvt( void )
  304.     {
  305.         // event record contains window ptr
  306.             fWhichWindow = (WindowPtr) fTheEvent.message;
  307.  
  308.         // see if window belongs to a document
  309.             fCurDoc = fDocList->FindDoc( fWhichWindow );
  310.             SetPort( fWhichWindow );
  311.     
  312.         if ( fCurDoc != nil )
  313.           fCurDoc->DoActivate(( fTheEvent.modifiers & activeFlag ) != 0 );
  314.     
  315.     }  /* TApplication::DoActivateEvt */
  316.  
  317.  
  318. //-----------------------------------------------------------------------
  319. // TApplication::DoUpdateEvt -     a window needs to be updated. If it is one
  320. //                                of ours, call the document's update routine
  321. //                                to redraw it.
  322. //
  323.     void TApplication::DoUpdateEvt(void)
  324.     {
  325.         // event record contains window ptr
  326.             fWhichWindow = (WindowPtr) fTheEvent.message;
  327.  
  328.         // see if window belongs to a document
  329.             fCurDoc = fDocList->FindDoc( fWhichWindow );
  330.             SetPort( fWhichWindow );
  331.         
  332.             if ( fCurDoc != nil )
  333.                 fCurDoc->DoUpdate();
  334.           
  335.     }  /* TApplication::DoUpdateEvt */
  336.  
  337.  
  338. //-----------------------------------------------------------------------
  339. // TApplication::DoSuspend -     although our suspend and resume routine are
  340. //                                identical, let's use two distinct routines
  341. //                                so that a subclass can choose to override one
  342. //                                without having to rewrite the other.
  343. //
  344.     void TApplication::DoSuspend( Boolean doClipConvert )
  345.     {
  346.         doClipConvert = false;        // this is here because I HATE compiler warnings!!
  347.         if (fCurDoc != nil)
  348.           fCurDoc->DoActivate( !fInBackground );
  349.           
  350.     }  /* TApplication::DoSuspend */
  351.  
  352.  
  353. //-----------------------------------------------------------------------
  354. // TApplication::DoResume -     although our suspend and resume routine are
  355. //                                identical, let's use two distinct routines
  356. //                                so that a subclass can choose to override one
  357. //                                without having to rewrite the other.
  358. //
  359.     void TApplication::DoResume( Boolean doClipConvert )
  360.     {
  361.         doClipConvert = false;        // this is here because I HATE compiler warnings!!
  362.         if ( fCurDoc != nil )
  363.           fCurDoc->DoActivate( !fInBackground );
  364.           
  365.     }  /* TApplication::DoResume */
  366.  
  367.  
  368. //-----------------------------------------------------------------------
  369. // TApplication::DoOSEvent -     handle multifinder events accordingly.
  370. //
  371.     void TApplication::DoOSEvent(void)
  372.     {
  373.         Boolean doConvert;
  374.         unsigned char evType;
  375.     
  376.         // is it a multifinder event?
  377.             evType = (unsigned char) (fTheEvent.message >> 24) & 0x00ff;
  378.             switch ( evType )                    // high byte of message is type of event
  379.             {                                     
  380.                 case kMouseMovedMessage :        DoIdle();                    // mouse-moved is also an idle event
  381.                                                 break;
  382.                     
  383.                 case kSuspendResumeMessage :    doConvert = ( fTheEvent.message & kClipConvertMask ) != 0;
  384.                                                 fInBackground = ( fTheEvent.message & kResumeMask ) == 0;
  385.                                                 
  386.                                                 // depending on whether or not we're in the background call suspend or resume
  387.                                                     if ( fInBackground )
  388.                                                         DoSuspend( doConvert );
  389.                                                     else
  390.                                                         DoResume( doConvert );
  391.                                                 
  392.                                                 break;
  393.             }
  394.         
  395.     }  /* TApplication::DoOSEvent */
  396.  
  397.  
  398. //-----------------------------------------------------------------------
  399. // TApplication::DoMouseDown -     simple routine to handle a mouse click.
  400. //
  401.     void TApplication::DoMouseDown( void )
  402.     {
  403.         long        mResult;
  404.         short        partCode;
  405.         WindowPtr    tWind;
  406.         EventRecord    tEvt;
  407.     
  408.         // gotta watch those object field dereferences
  409.             partCode = FindWindow( fTheEvent.where, &tWind );
  410.             fWhichWindow = tWind;
  411.             tEvt = fTheEvent;
  412.             switch ( partCode )
  413.             {
  414.                 case inSysWindow :    DoMouseInSysWindow();
  415.                                     break;
  416.                                     
  417.                 case inMenuBar :    AdjustMenus();
  418.                                     mResult = MenuSelect( tEvt.where );
  419.                                     if ( mResult != 0 )
  420.                                       DoMenuCommand(HiWrd( mResult ), LoWrd( mResult ));
  421.                                     break;
  422.                                     
  423.                 case inGoAway :        DoGoAway();                    
  424.                                     break;
  425.                                     
  426.                 case inDrag :        DoDrag();
  427.                                     break;
  428.                                     
  429.                 case inGrow :        if ( fCurDoc != nil )
  430.                                           fCurDoc->DoGrow( &tEvt );                    
  431.                                     break;
  432.                 case inZoomIn :
  433.                 case inZoomOut :    if (( TrackBox(fWhichWindow, tEvt.where, partCode )) && ( fCurDoc != nil ))
  434.                                         fCurDoc->DoZoom(partCode);
  435.                                     break;
  436.                                     
  437.                 case inContent :    // If window is not in front, make it so
  438.                                         if ( fWhichWindow != FrontWindow() )
  439.                                             SelectWindow(fWhichWindow);
  440.                                         else
  441.                                             if (fCurDoc != nil)
  442.                                                  fCurDoc->DoContent(&tEvt);                    
  443.                                     break;
  444.             }
  445.           
  446.     }  /* TApplication::DoMouseDown */
  447.  
  448.  
  449. //-----------------------------------------------------------------------
  450. // TApplication::DoDrag -     have the system track the mouse movements and
  451. //                            drag the window accordingly.
  452. //
  453.     void TApplication::DoDrag( void )
  454.     {
  455.         DragWindow( fWhichWindow, fTheEvent.where, &qd.screenBits.bounds );
  456.         
  457.     }  /* TApplication::DoDrag */
  458.  
  459.  
  460. //-----------------------------------------------------------------------
  461. // TApplication::DoGoAway -     a mousedown event occurred in the go away region
  462. //                                of the active window; continue tracking it. If
  463. //                                the mouse is released in the region, then close
  464. //                                the window.
  465. //
  466.     void TApplication::DoGoAway( void )
  467.     {
  468.         if ( TrackGoAway( fWhichWindow, fTheEvent.where ))            // was the mouse released in the goAway region?
  469.         {                                                            // yes, but is the window ours?
  470.             if ( fCurDoc != nil )
  471.             {                                                        // it is our window, let's close it
  472.                 fDocList->RemoveDoc( fCurDoc );
  473.                 fCurDoc->DoClose();
  474.             }
  475.             else                                                    // no, the window must belong to the system, let them handle it
  476.                 CloseDeskAcc(((WindowPeek) fWhichWindow )->windowKind );
  477.                 
  478.             // make sure our current document/window references are valid
  479.                 if ( fWhichWindow != nil )                            // does an active window remain? 
  480.                 {
  481.                     fCurDoc = fDocList->FindDoc( fWhichWindow );    // yes, update the current document pointer
  482.                     SetPort( fWhichWindow );                        // be sure to make future drawings to the new window
  483.                 }
  484.                 else
  485.                     fCurDoc = nil;                                    // no active window remains
  486.         }
  487.     }  /* TApplication::DoGoAway */
  488.  
  489.  
  490. //-----------------------------------------------------------------------
  491. // TApplication::TrapAvailable -     Check and see if the trap exists. 
  492. //
  493.     Boolean TApplication::TrapAvailable(short tNumber,TrapType tType)
  494.     {
  495.         // On 64K ROM machines, tType will be ignored.
  496.             return NGetTrapAddress( tNumber, tType ) != GetTrapAddress( _Unimplemented );
  497.         
  498.     }  /* TApplication::TrapAvailable */
  499.  
  500.  
  501. //-----------------------------------------------------------------------
  502. // TApplication::AlertUser -     Simple routine to display an alert dialog
  503. //                                using str# resources. Although this routine
  504. //                                exists and is used in this sample, a more
  505. //                                streamlined approach would be to create a
  506. //                                dialog library (or even an object) which
  507. //                                could be linked in. Using such a method,only
  508. //                                one source copy would exist.
  509. //
  510.     void TApplication::AlertUser( short errResID, short errCode )
  511.     {
  512.         Str255 message;
  513.     
  514.         SetCursor( &qd.arrow );
  515.         GetIndString( message, errResID, errCode );
  516.         ParamText( message, "\p", "\p", "\p" );
  517.         (void) Alert( rUserAlert, (ModalFilterProcPtr) nil);
  518.         
  519.     }  /* TApplication::AlertUser */
  520.  
  521. //-----------------------------------------------------------------------
  522. // TApplication::BigBadError -     A variation of AlertUser which aborts to
  523. //                                the finder. 
  524. //
  525.     void TApplication::BigBadError(short errResID, short errCode)
  526.     {
  527.         AlertUser( errResID,errCode );
  528.         ExitToShell();
  529.         
  530.     }  /* TApplication::BigBadError */
  531.     
  532. // That's all, folks...
  533.